<template>
  <div class="video-anomaly-detection-system">
    <!-- 头部导航 -->
    <header class="header">
      <div class="logo">
        <i>📊</i>
        <span>视频异常检测分析系统</span>
      </div>
      <div class="nav-right">
        <div class="nav-item">
          <i>⚙️</i>
          <span>设置</span>
        </div>
        <div class="nav-item">
          <i>❓</i>
          <span>帮助</span>
        </div>
        <div class="nav-item">
          <i>📧</i>
          <span>display@ai.com</span>
        </div>
      </div>
    </header>

    <!-- 主容器 -->
    <div class="container">
      <!-- 侧边栏 -->
    <aside class="sidebar">
      <h3>视频选择</h3>
      <select class="form-control" v-model="selectedVideo" @click="loadOnlineVideos" @change="selectVideo">
        <option v-for="video in onlineVideos" :key="video.label" :value="video">
          {{ video.label }}
        </option>
        <option v-if="onlineVideos.length === 0" disabled>加载中...</option>
      </select>

        <h3>异常阈值</h3>
        <div class="slider-container">
          <span>{{ anomalyThreshold }}%</span>
          <input type="range" min="0" max="100" v-model.number="anomalyThreshold" class="slider" id="threshold-slider">
        </div>

        <div class="btn-group">
          <button class="btn btn-primary" @click="playVideo">
            <i>▶️</i> 播放
          </button>
          <button class="btn btn-secondary" @click="exportReport">
            <i>📤</i> 导出报告
          </button>
        </div>

        <div class="performance-card">
          <h4>模型性能指标</h4>
          <div class="metrics">
            <div class="metric">
              <div class="metric-value">{{ aucScore }}</div>
              <div class="metric-label">AUC</div>
            </div>
            <div class="metric">
              <div class="metric-value">{{ apScore }}</div>
              <div class="metric-label">AP</div>
            </div>
          </div>
        </div>

        <!-- 视频选择器已移除，通过侧边栏顶部的视频选择下拉菜单选择视频 -->

        <!-- 帧检测控制 -->
        <div class="frame-detection-controls">
          <h3>帧检测设置</h3>
          <div class="setting-item">
            <label>目标帧数：</label>
            <input type="number" v-model.number="targetFrame" min="1" :max="totalFrames" class="frame-input">
          </div>
          <div class="setting-item">
            <label>容差范围：</label>
            <input type="number" v-model.number="frameTolerance" min="0" max="10" class="tolerance-input">
            <span class="unit">帧</span>
          </div>
          <div class="setting-item">
            <label>启用帧捕获：</label>
            <input type="checkbox" v-model="enableFrameCapture" class="frame-capture-checkbox">
          </div>

          <!-- 关键帧管理 -->
          <div class="keyframe-management">
            <h5>关键帧管理</h5>
            <div class="keyframe-input-group">
              <input type="number" v-model.number="newKeyframe" min="1" :max="totalFrames" placeholder="输入关键帧号"
                class="keyframe-input">
              <button @click="addKeyframe" class="add-keyframe-btn">添加</button>
            </div>
            <div class="keyframe-list">
              <div v-for="(frame, index) in targetFrames" :key="index" class="keyframe-item">
                <span class="keyframe-number">第 {{ frame }} 帧</span>
                <button @click="removeKeyframe(index)" class="remove-keyframe-btn">删除</button>
                <button @click="seekToFrame(frame)" class="seek-keyframe-btn">跳转</button>
              </div>
            </div>
          </div>

          <button @click="seekToFrame(targetFrame)" class="seek-btn">跳转到帧</button>
          <button @click="showExactFrameInfo = !showExactFrameInfo" class="info-btn">
            {{ showExactFrameInfo ? '隐藏' : '显示' }}精确帧信息
          </button>
        </div>
      </aside>

      <!-- 主内容区 -->
      <main class="main-content">
        <!-- 视频流分析 -->
        <div class="video-header">
          <div class="video-title">视频流分析</div>
          <div class="status-badge" :class="analysisStatusClass">{{ analysisStatus }}</div>
          <div class="detection-time">检测时间：{{ detectionTime }}</div>
        </div>

        <!-- 视频播放器 -->
        <div class="video-container">
          <video ref="videoPlayer" class="video-player" controls :key="videoSrc" @loadedmetadata="onVideoLoaded"
            @timeupdate="onTimeUpdate" @play="onVideoPlay" @pause="onVideoPause" @ended="onVideoEnded"
            @error="onVideoError" @loadeddata="onVideoDataLoaded" @loadstart="onVideoLoadStart">
            <source :src="videoSrc" :type="getVideoType(videoSrc)">
            您的浏览器不支持视频播放。请尝试使用MP4格式或上传其他视频文件。
          </video>

          <div v-if="videoError" class="video-error">
            <p>❌ {{ videoError }}</p>
            <button @click="retryVideoLoad" class="retry-btn">重试</button>
            <button @click="showSampleVideo" class="sample-btn">使用示例视频</button>
          </div>

          <div class="frame-overlay" v-if="showFrameInfo">
            <div class="frame-info">
              <h3>检测到特定帧！</h3>
              <p>当前帧：{{ currentFrame }}</p>
              <p>时间：{{ currentTime.toFixed(2) }}秒</p>
              <p>帧信息：{{ getFrameInfo() }}</p>
            </div>
          </div>
        </div>

        <!-- 视频信息显示区域 -->
        <div class="video-info">
          {{ currentVideoName || getVideoNameFromPath() }} - 当前帧：<span
            style="font-size: 18px; font-weight: bold; color: #ff4757;">{{ currentFrame }}</span> | 时间：{{
              formatTime(currentTime) }}
        </div>



        <!-- 精确帧信息 -->
        <div class="exact-frame-info-card" v-if="showExactFrameInfo">
          <h4>精确帧信息</h4>
          <div class="frame-detail">
            <p><strong>精确帧号：</strong> {{ exactFrameInfo.exactFrame.toFixed(2) }}</p>
            <p><strong>当前帧内进度：</strong> {{ (exactFrameInfo.frameProgress * 100).toFixed(1) }}%</p>
            <p><strong>是否关键帧：</strong> {{ exactFrameInfo.isKeyFrame ? '是' : '否' }}</p>
            <p><strong>精确时间：</strong> {{ exactFrameInfo.time.toFixed(3) }}s</p>
          </div>
        </div>

        <!-- 分析结果 -->
        <div class="analysis-sections">
          <!-- 异常事件分析 -->
          <div class="analysis-card">
            <h3>异常事件分析</h3>

            <div v-if="currentFrameAnomalyEvent" class="event-item">
              <div class="event-title">
                <span>{{ currentFrameAnomalyEvent.eventName || currentFrameAnomalyEvent.title }}</span>
                <span><span class="frame-number" @click="seekToFrame(currentFrameAnomalyEvent.frameNo || currentFrameAnomalyEvent.frame)" style="cursor: pointer; color: #007bff; text-decoration: underline;">第{{ currentFrameAnomalyEvent.frameNo || currentFrameAnomalyEvent.frame }}帧</span> | {{
                  currentFrameAnomalyEvent.time || currentFrameAnomalyEvent.timeRange }}</span>
              </div>
              <div class="event-description">{{ currentFrameAnomalyEvent.description }}</div>
              <div class="progress-bar">
                <div class="progress-fill"
                  :style="{ width: (currentFrameAnomalyEvent.confidence || currentFrameAnomalyEvent.level) + '%', backgroundColor: getEventColor(currentFrameAnomalyEvent.confidence || currentFrameAnomalyEvent.level) }">
                </div>
              </div>
              <div class="progress-text">{{ (currentFrameAnomalyEvent.confidence || currentFrameAnomalyEvent.level) }}%
              </div>
            </div>

            <div v-else-if="currentFrame > 0" class="no-events">
              <p>当前播放位置之前暂无异常事件检测到</p>
            </div>
            <div v-else class="no-events">
              <p>暂无异常事件检测到</p>
            </div>
          </div>

          <!-- 异常深度分析 -->
          <div class="analysis-card">
            <h3>异常深度分析</h3>

            <div v-if="currentHighRiskEvent" class="high-risk-event">
              <div class="high-risk-title">高危事件 {{ currentHighRiskEvent.eventName || currentHighRiskEvent.title }}</div>
              <div class="high-risk-time"><span class="frame-number" @click="seekToFrame(currentHighRiskEvent.frameNo || currentHighRiskEvent.frame)" style="cursor: pointer; color: #007bff; text-decoration: underline;">第{{ currentHighRiskEvent.frameNo || currentHighRiskEvent.frame }}帧</span> | {{
                currentHighRiskEvent.time || currentHighRiskEvent.timeRange }} | 置信度：{{ currentHighRiskEvent.confidence
                  || currentHighRiskEvent.level }}%</div>

              <div class="event-desc-title">事件描述</div>
              <div class="event-desc-content">
                {{ currentHighRiskEvent.description }}
              </div>

              <div v-if="currentHighRiskEvent.summary" class="event-summary-title">事件总结</div>
              <div v-if="currentHighRiskEvent.summary" class="event-summary-content">
                {{ currentHighRiskEvent.summary }}
              </div>
            </div>

            <div v-else class="no-high-risk">
              <p>当前无高危异常事件</p>
            </div>
          </div>
        </div>

        <!-- 异常分数曲线图 - 使用新的图表组件 -->
        <AnomalyChart :anomaly-scores="anomalyScores" :threshold="anomalyThreshold" :current-frame="currentFrame"
          @refresh="refreshChart" />
      </main>
    </div>
  </div>
</template>

<script>
import { getSourceInfo, getViewList } from './api/index.js';
import AnomalyChart from './components/AnomalyChart.vue';

export default {
  name: 'VideoAnomalyDetectionSystem',
  components: {
    AnomalyChart
  },
  data() {
    return {
      // 原有视频播放数据
      videoSrc: 'https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_1MB.mp4',
      currentVideoName: '',
      currentFrame: 0,
      totalFrames: 0,
      currentTime: 0,
      duration: 0,
      frameRate: 25,
      targetFrame: 100,
      frameTolerance: 2,
      targetFrames: [],
      showFrameInfo: false,
      videoError: '',
      onlineVideoUrl: '',
      newKeyframe: 1,
      exactFrameInfo: null,
      showExactFrameInfo: false,
      lastTriggeredFrame: -1,
      animationFrameId: null,
      isProcessingFrame: false,
      enableFrameCapture: false,
      lastDisplayedFrame: -1,

      // 新增异常检测数据
      selectedDataset: 'XD-Violence',
      selectedVideo: null,
      anomalyThreshold: 60,
      analysisStatus: '就绪中',
      detectionTime: new Date().toLocaleString('zh-CN'),
      aucScore: '0.924',
      apScore: '0.788',

      // 异常事件数据
      anomalyEvents: [],
      currentHighRiskEvent: null,

      // 图表数据
      chartXAxis: ['00:00', '00:30', '01:00', '01:30', '02:00'],
      anomalyScores: [],
      chartDataPoints: [], // 存储包含帧号和分数的完整数据点，用于图表绘制

      // 模拟数据生成
      mockAnomalyInterval: null,

      // 新增在线视频列表数据
      onlineVideos: [],
      viewListData: [],
      isLoadingVideos: false,
      videoLoadError: ''
    }
  },
  computed: {
    analysisStatusClass() {
      return {
        'status-ready': this.analysisStatus === '就绪中',
        'status-analyzing': this.analysisStatus === '分析中',
        'status-detected': this.analysisStatus === '检测到异常'
      };
    },
    // 获取当前播放帧之前或等于当前播放帧的最后一条异常事件
    currentFrameAnomalyEvent() {
      // 筛选出帧号小于或等于当前播放帧的所有异常事件
      const relevantEvents = this.anomalyEvents.filter(event => {
        const eventFrame = parseInt(event.frameNo || event.frame || 0);
        return eventFrame <= this.currentFrame;
      });

      // 返回最后一条异常事件，如果没有则返回null
      return relevantEvents.length > 0 ? relevantEvents[relevantEvents.length - 1] : null;
    },
    getChartBackground() {
      // 修复坐标计算逻辑，使用百分比坐标系
      if (!this.anomalyScores || this.anomalyScores.length === 0 || !this.chartDataPoints || this.chartDataPoints.length === 0) {
        return 'transparent';
      }

      try {
        // 获取数据点的最小和最大帧号，用于计算x坐标的比例
        let minFrame = Infinity;
        let maxFrame = -Infinity;
        for (const point of this.chartDataPoints) {
          minFrame = Math.min(minFrame, point.frame_no);
          maxFrame = Math.max(maxFrame, point.frame_no);
        }

        // 避免除零错误
        const frameRange = maxFrame > minFrame ? maxFrame - minFrame : 1;

        // 创建SVG，使用视口坐标系
        let svg = '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 100 100">';

        // 生成路径（使用直线连接）
        svg += '<path d="';
        for (let i = 0; i < this.chartDataPoints.length; i++) {
          const point = this.chartDataPoints[i];

          // 计算x坐标（使用百分比，0-100）
          const margin = 0; // 移除边距，确保精确映射
          const x = margin + ((point.frame_no - minFrame) / frameRange) * (100 - margin * 2);

          // 分数范围是0-100，精确映射到Y轴
          const score = Math.max(0, Math.min(100, point.score));
          // 精确Y轴映射：确保score值与图表刻度完全对应
          // 图表刻度：底部0，顶部100，Y轴向上增加
          // SVG坐标系：左上角(0,0)，Y轴向下增加
          // 因此需要反转：score=0对应y=100，score=100对应y=0
          // 确保score=10对应y=90，score=90对应y=10
          const y = 100 - score;

          if (i === 0) {
            svg += 'M ' + x + ' ' + y;
          } else {
            svg += ' L ' + x + ' ' + y;
          }
        }
        svg += '" stroke="#00b662" stroke-width="0.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>';

        // 生成数据点
        for (const point of this.chartDataPoints) {
          const margin = 0; // 移除边距，确保精确映射
          const x = margin + ((point.frame_no - minFrame) / frameRange) * (100 - margin * 2);
          const score = Math.max(0, Math.min(100, point.score));
          // 使用相同的Y轴映射逻辑，确保数据点与路径一致
          const y = 100 - score; // 保持Y轴反转
          svg += '<circle cx="' + x + '" cy="' + y + '" r="0.5" fill="#00b662"/>';
        }

        svg += '</svg>';

        // base64编码
        const encoded = btoa(unescape(encodeURIComponent(svg)));
        return 'url("data:image/svg+xml;base64,' + encoded + '")';
      } catch (e) {
        console.error('图表生成错误', e);
        return 'transparent';
      }
    }
  },
  methods: {
    // 原有视频播放方法
    onVideoLoaded() {
      const video = this.$refs.videoPlayer;
      this.duration = video.duration;
      this.frameRate = 25; // 默认帧率，可以根据需要调整
      this.totalFrames = Math.floor(this.duration * this.frameRate);
      console.log(`视频加载完成 - 总时长: ${this.duration}s, 总帧数: ${this.totalFrames}, 帧率: ${this.frameRate}`);
    },

    onTimeUpdate() {
      const video = this.$refs.videoPlayer;
      this.currentTime = video.currentTime;

      // 计算当前帧数（基于帧率）
      const newFrame = Math.floor(this.currentTime * this.frameRate);

      // 优化控制台输出 - 只在帧数变化时输出
      if (newFrame !== this.lastDisplayedFrame) {
        this.lastDisplayedFrame = newFrame;
        this.currentFrame = newFrame;
        // console.log(`⏰ 时间更新 - 当前时间: ${this.currentTime.toFixed(3)}s, 当前帧: ${this.currentFrame}`);
        // 更新异常深度分析
        this.updateAnomalyEventsList();
      }

      // 检查目标帧
      this.checkTargetFrames();
    },

    onVideoPlay() {
      console.log('▶️ 视频开始播放');
      this.startSmoothFrameUpdate();
    },

    onVideoPause() {
      console.log('⏸️ 视频暂停');
      this.stopSmoothFrameUpdate();
    },

    onVideoEnded() {
      console.log('🏁 视频播放结束');
      this.stopSmoothFrameUpdate();
    },

    onVideoError(e) {
      console.error('❌ 视频加载错误:', e);
      this.videoError = '视频加载失败，请检查视频源或尝试其他格式';
    },

    onVideoDataLoaded() {
      console.log('📦 视频数据加载完成');
    },

    onVideoLoadStart() {
      console.log('🚀 开始加载视频');
    },

    getVideoType(src) {
      if (src.includes('.mp4')) return 'video/mp4';
      if (src.includes('.avi')) return 'video/avi';
      if (src.includes('.webm')) return 'video/webm';
      return 'video/mp4';
    },

    getVideoNameFromPath() {
      const path = this.videoSrc;
      if (!path) return '未选择视频';
      const parts = path.split('/');
      const filename = parts[parts.length - 1];
      return decodeURIComponent(filename);
    },

    formatTime(seconds) {
      const mins = Math.floor(seconds / 60);
      const secs = Math.floor(seconds % 60);
      return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
    },

    loadOnlineVideo() {
      if (this.onlineVideoUrl) {
        this.videoSrc = this.onlineVideoUrl;
        this.currentVideoName = '';
        this.videoError = '';
        console.log('加载在线视频:', this.onlineVideoUrl);
      }
    },

    loadLocalVideo(path) {
      this.videoSrc = path;
      this.currentVideoName = '';
      this.videoError = '';
      console.log('加载本地视频:', path);

      // 从路径中提取视频名称
      const videoName = path.split('/').pop().split('.')[0];
      console.log('提取的视频名称:', videoName);

      // 清除原有异常数据
      this.viewListData = [];
      this.anomalyScores = [];
      this.anomalyEvents = [];
      this.currentHighRiskEvent = null;

      // 加载对应视频的异常数据
      this.loadViewListData(videoName);
    },

    onVideoSelect(event) {
      const file = event.target.files[0];
      if (file) {
        const url = URL.createObjectURL(file);
        this.videoSrc = url;
        this.currentVideoName = file.name;
        this.videoError = '';
        console.log('选择本地文件:', file.name);
      }
    },

    retryVideoLoad() {
      const video = this.$refs.videoPlayer;
      video.load();
      this.videoError = '';
    },

    showSampleVideo() {
      this.loadLocalVideo('/video/Danmaku%20Genesis%202025-08-20%2021-25-35.mp4');
    },

    // 帧检测相关方法
    startSmoothFrameUpdate() {
      if (this.animationFrameId) {
        cancelAnimationFrame(this.animationFrameId);
      }

      const updateFrame = () => {
        if (this.$refs.videoPlayer && !this.$refs.videoPlayer.paused) {
          const video = this.$refs.videoPlayer;
          const currentTime = video.currentTime;

          // 计算更精确的帧信息
          const exactFrame = currentTime * this.frameRate;
          const frameProgress = (exactFrame - Math.floor(exactFrame));
          const isKeyFrame = frameProgress < 0.1 || frameProgress > 0.9;

          // 更新精确帧信息
          this.exactFrameInfo = {
            exactFrame: exactFrame,
            frameProgress: frameProgress,
            isKeyFrame: isKeyFrame,
            time: currentTime
          };

          // 只在帧数变化时更新显示和控制台输出
          const newFrame = Math.floor(exactFrame);
          if (newFrame !== this.currentFrame) {
            this.currentFrame = newFrame;
            // 更新异常深度分析
            this.updateAnomalyEventsList();
            // 检查目标帧
            this.checkTargetFrames();
          }

          // console.log(`🎬 平滑更新: ${currentTime.toFixed(3)}s, 精确帧: ${exactFrame.toFixed(2)}, 显示帧: ${this.currentFrame}`);
        }

        this.animationFrameId = requestAnimationFrame(updateFrame);
      };

      updateFrame();
    },

    stopSmoothFrameUpdate() {
      if (this.animationFrameId) {
        cancelAnimationFrame(this.animationFrameId);
        this.animationFrameId = null;
      }
    },

    checkTargetFrames() {
      // 防止同一帧重复处理
      if (this.isProcessingFrame) {
        return
      }

      const tolerance = this.frameTolerance

      // 详细的控制台输出 - 每次检测都输出
      console.log(`🔍 帧检测开始 - 当前帧: ${this.currentFrame}, 容差: ${tolerance}, 目标帧列表: [${this.targetFrames.join(', ')}]`)

      for (let targetFrame of this.targetFrames) {
        const frameDiff = Math.abs(this.currentFrame - targetFrame)
        const isInRange = frameDiff <= tolerance

        // 输出每个目标帧的检测详情
        console.log(`  📊 检测目标帧 ${targetFrame}: 差值=${frameDiff}, 在范围内=${isInRange}, 上次触发=${this.lastTriggeredFrame}`)

        if (isInRange) {
          // 防止重复触发同一个帧 - 容差为0时允许重新触发同一帧（用于精确检测）
          if (this.lastTriggeredFrame !== targetFrame || tolerance === 0) {
            this.isProcessingFrame = true // 设置处理中状态
            this.lastTriggeredFrame = targetFrame
            this.showFrameInfo = true
            this.showFrameInfoForDuration(2000) // 显示2秒

            // 记录详细的帧信息
            const exactTime = targetFrame / this.frameRate
            console.log(`🎬 ✅ 触发帧检测成功！目标帧 ${targetFrame}，当前帧 ${this.currentFrame}，精确时间 ${exactTime.toFixed(3)}s`)
            console.log(`   📈 详细信息: 容差=${tolerance}, 差值=${frameDiff}, 帧率=${this.frameRate}`)

            // 如果需要，可以在这里添加帧捕获逻辑
            // 注意：跨域视频可能无法捕获帧，这是浏览器的安全限制
            if (this.enableFrameCapture) {
              this.captureSpecificFrame(targetFrame).then((frameData) => {
                // 帧捕获完成后重置处理状态
                setTimeout(() => {
                  this.isProcessingFrame = false
                }, 100) // 稍微延迟重置，避免立即重复触发
              })
            } else {
              // 如果禁用帧捕获，直接重置处理状态
              setTimeout(() => {
                this.isProcessingFrame = false
              }, 100)
            }

          } else {
            console.log(`   ⚠️  目标帧 ${targetFrame} 已触发过，跳过以避免重复`)
          }
          break
        }
      }

      // 如果离开触发范围，重置lastTriggeredFrame
      let inRange = false
      for (let targetFrame of this.targetFrames) {
        if (Math.abs(this.currentFrame - targetFrame) <= tolerance) {
          inRange = true
          break
        }
      }
      if (!inRange && this.lastTriggeredFrame !== -1) {
        console.log(`🔄 离开所有目标帧范围，重置触发状态`)
        this.lastTriggeredFrame = -1
        this.isProcessingFrame = false // 离开范围时重置处理状态
      }
    },

    showFrameInfoForDuration(duration) {
      setTimeout(() => {
        this.showFrameInfo = false;
      }, duration);
    },

    async captureSpecificFrame(targetFrame) {
      try {
        const video = this.$refs.videoPlayer;
        if (!video) return null;

        // 创建canvas来捕获帧
        const canvas = document.createElement('canvas');
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        const ctx = canvas.getContext('2d');

        // 绘制当前帧到canvas
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

        try {
          // 尝试转换为base64
          const dataURL = canvas.toDataURL('image/png');
          console.log(`📸 帧捕获成功 - 目标帧 ${targetFrame}, 数据大小: ${dataURL.length} 字符`);
          return dataURL;
        } catch (toDataURLError) {
          // 捕获跨域错误
          if (toDataURLError.name === 'SecurityError') {
            console.warn(`⚠️ 跨域限制：无法捕获帧 ${targetFrame}，视频源可能存在跨域限制`);
          } else {
            console.warn(`⚠️ 帧捕获失败 - 目标帧 ${targetFrame}:`, toDataURLError.message);
          }
          return null;
        }
      } catch (error) {
        console.error(`❌ 帧捕获过程出错 - 目标帧 ${targetFrame}:`, error);
        return null;
      }
    },

    getFrameInfo() {
      if (!this.exactFrameInfo) return '加载中...';
      return `精确帧 ${this.exactFrameInfo.exactFrame.toFixed(2)} | 进度 ${(this.exactFrameInfo.frameProgress * 100).toFixed(1)}%`;
    },

    seekToFrame(frameNumber) {
      const video = this.$refs.videoPlayer;
      if (video && this.frameRate > 0) {
        const targetTime = frameNumber / this.frameRate;
        video.currentTime = Math.min(targetTime, this.duration);
        // 跳转时自动暂停视频
        video.pause();
        console.log(`🎯 跳转到帧 ${frameNumber} (时间: ${targetTime.toFixed(3)}s)，视频已暂停`);
      }
    },

    addTargetFrame() {
      if (this.targetFrame > 0 && this.targetFrame <= this.totalFrames) {
        if (!this.targetFrames.includes(this.targetFrame)) {
          this.targetFrames.push(this.targetFrame);
          this.targetFrames.sort((a, b) => a - b);
          console.log(`➕ 添加目标帧: ${this.targetFrame}`);
        } else {
          console.log(`⚠️ 目标帧 ${this.targetFrame} 已存在`);
        }
      }
    },

    removeTargetFrame(index) {
      const removed = this.targetFrames.splice(index, 1);
      console.log(`🗑️ 删除目标帧: ${removed[0]}`);
    },

    addKeyframe() {
      if (this.newKeyframe > 0 && this.newKeyframe <= this.totalFrames) {
        if (!this.targetFrames.includes(this.newKeyframe)) {
          this.targetFrames.push(this.newKeyframe);
          this.targetFrames.sort((a, b) => a - b);
          console.log(`➕ 添加关键帧: ${this.newKeyframe}`);
        } else {
          console.log(`⚠️ 关键帧 ${this.newKeyframe} 已存在`);
        }
      }
    },

    removeKeyframe(index) {
      const removed = this.targetFrames.splice(index, 1);
      console.log(`🗑️ 删除关键帧: ${removed[0]}`);
    },

    // 播放视频
    playVideo() {
      const video = this.$refs.videoPlayer;
      if (video) {
        video.play().catch(error => {
          console.error('播放视频失败:', error);
          this.videoError = '无法播放视频，请检查浏览器权限';
        });
      } else {
        console.warn('视频元素未找到');
      }
    },

    // 新增异常检测方法
    startAnalysis() {
      this.analysisStatus = '分析中';
      console.log('🔍 开始异常检测分析');

      // 模拟分析过程
      setTimeout(() => {
        this.generateMockAnomalyEvents();
        this.analysisStatus = '分析完成';
      }, 2000);
    },

    // 选择视频
    selectVideo() {
      if (this.selectedVideo) {
        const video = this.selectedVideo;
        this.videoSrc = video.source.value;
        this.currentVideoName = video.label;
        this.videoError = '';
        console.log('选择视频:', video.label, video.source.value);

        // 清除所有异常分析相关数据
        this.viewListData = [];
        this.anomalyScores = [];
        this.anomalyEvents = [];
        this.currentHighRiskEvent = null;
        this.chartDataPoints = [];
        this.analysisStatus = '就绪中';

        // 加载对应的事件数据，传入视频名称
        this.loadViewListData(video.label);
      }
    },

    // 选择在线视频（保留兼容性）
    selectOnlineVideo(video) {
      this.selectedVideo = video;
      this.videoSrc = video.source.value;
      this.currentVideoName = video.label;
      this.videoError = '';
      console.log('选择在线视频:', video.label, video.source.value);

      // 清除所有异常分析相关数据
      this.viewListData = [];
      this.anomalyScores = [];
      this.anomalyEvents = [];
      this.currentHighRiskEvent = null;
      this.chartDataPoints = [];
      this.analysisStatus = '就绪中';

      // 加载对应的事件数据，传入视频名称
      this.loadViewListData(video.label);
    },

    // 加载在线视频列表
    async loadOnlineVideos() {
      this.isLoadingVideos = true;
      this.videoLoadError = '';

      try {
        const response = await getSourceInfo();
        console.log('API响应数据:', response);
        // 适配API返回格式，code是"0"表示成功
        if ((response.code === 'success' || response.code === '0') && response.data && Array.isArray(response.data)) {
          // 处理数据格式，将source.value转换为嵌套结构，并移除可能的反引号
          this.onlineVideos = response.data.map(item => {
            // 处理source.value字段，移除可能的反引号和多余空格
            let sourceValue = item['source.value'] || (item.source?.value || '');
            // 移除可能的反引号
            sourceValue = sourceValue.toString().replace(/`/g, '').trim();

            // 将外网地址替换为内网地址
            sourceValue = sourceValue.replace(/36\.248\.25\.30/g, '192.168.19.138');

            // 确保正确获取label，特别是数字开头的label
            let labelValue = item.label;
            // 如果item.label不存在，尝试检查其他可能的label字段
            if (!labelValue) {
              // 遍历item的所有属性，查找可能的label字段
              for (const key in item) {
                if (key.includes('label') || key === '08dog') {
                  labelValue = item[key];
                  break;
                }
              }
            }

            return {
              label: labelValue || '未知视频',
              source: { value: sourceValue },
              value: item.value
            };
          });
          console.log('✅ 在线视频列表加载成功:', this.onlineVideos.length, '个视频');
          console.log('在线视频列表数据:', this.onlineVideos);
          this.videoLoadError = '';
        } else {
          this.videoLoadError = '加载视频列表失败';
          console.error('❌ 加载在线视频列表失败:', response);
        }
      } catch (error) {
        this.videoLoadError = '加载视频列表出错: ' + error.message;
        console.error('❌ 加载在线视频列表出错:', error);
      } finally {
        this.isLoadingVideos = false;
      }
    },

    // 加载事件数据
    async loadViewListData(videoName = '') {
      try {
        console.log('📡 调用view_list接口，视频名称:', videoName);
        const response = await getViewList(videoName);
        console.log('view_list接口响应:', response);

        // 处理各种可能的返回格式
        if (response.code === '1' || response.code === 1) {
          // 处理code为1的情况，可能是嵌套data结构
          if (response.data && Array.isArray(response.data)) {
            this.viewListData = response.data;
          } else if (response.data && response.data.data && Array.isArray(response.data.data)) {
            // 处理嵌套data结构 {data: {data: [...]}} 
            this.viewListData = response.data.data;
          } else if (response.list && Array.isArray(response.list)) {
            this.viewListData = response.list;
          } else {
            console.error('❌ 数据格式异常:', response);
            this.viewListData = [];
          }

          if (this.viewListData.length > 0) {
            console.log('✅ 事件数据加载成功:', this.viewListData.length, '个事件');
            // 处理事件数据
            this.processViewListData();
          } else {
            console.log('⚠️  事件数据为空数组');
            // 为空时，使用特定视频的模拟数据
            this.generateMockAnomalyScores(videoName);
            this.generateMockAnomalyEvents(videoName);
          }
        } else if (response.code === '200' && response.data) {
          // 适配返回code为200的情况
          this.viewListData = response.data;
          console.log('✅ 事件数据加载成功:', this.viewListData.length, '个事件');

          // 处理事件数据
          this.processViewListData();
        } else if ((response.code === 'success' || response.code === 1) && response.list) {
          // 适配返回list字段的情况
          this.viewListData = response.list;
          console.log('✅ 事件数据加载成功:', this.viewListData.length, '个事件');

          // 处理事件数据
          this.processViewListData();
        } else if (response.code === '200' && response.list) {
          // 适配返回code为200且list字段的情况
          this.viewListData = response.list;
          console.log('✅ 事件数据加载成功:', this.viewListData.length, '个事件');

          // 处理事件数据
          this.processViewListData();
        } else {
          console.error('❌ 加载事件数据失败:', response);
          // 使用模拟数据作为备用，并传入视频名称
          this.generateMockAnomalyScores(videoName);
          this.generateMockAnomalyEvents(videoName);
        }
      } catch (error) {
        console.error('❌ 加载事件数据出错:', error);
        // 使用模拟数据作为备用，并传入视频名称
        this.generateMockAnomalyScores(videoName);
        this.generateMockAnomalyEvents(videoName);
      }
    },

    // 处理事件数据
    processViewListData() {
      if (!this.viewListData || this.viewListData.length === 0) {
        console.log('没有异常事件数据');
        return;
      }

      console.log('📊 开始处理异常事件数据，共', this.viewListData.length, '个事件');

      // 生成异常分数图表数据
      this.generateAnomalyScoresFromEvents();

      // 更新异常事件列表
      this.updateAnomalyEventsList();

      console.log('✅ 异常事件数据处理完成');
    },

    generateAnomalyScoresFromEvents() {
      console.log('🔍 开始生成异常分数图表数据');

      // 完全禁用默认数据，只使用真实数据
      if (!this.viewListData || this.viewListData.length === 0) {
        console.log('⚠️  没有异常事件数据，显示空图表');
        this.anomalyScores = [];
        this.chartXAxis = [];
        this.chartDataPoints = [];
        return;
      }

      console.log('📈 生成异常分数图表数据，事件数:', this.viewListData.length);
      console.log('📊 原始事件数据:', this.viewListData);

      // 按帧号排序事件
      const sortedEvents = this.viewListData.sort((a, b) => a.frame_no - b.frame_no);

      // 获取帧号范围
      const minFrame = Math.min(...sortedEvents.map(e => e.frame_no));
      const maxFrame = Math.max(...sortedEvents.map(e => e.frame_no));

      console.log(`📊 帧范围: ${minFrame} - ${maxFrame}`);

      // 直接使用实际的frame_no和level数据
      let dataPoints = [];
      let xAxisLabels = [];
      let chartDataPoints = [];

      console.log('📊 使用实际数据点，基于真实的frame_no和level值');

      // 为每个事件生成数据点
      sortedEvents.forEach(event => {
        // 直接使用level值乘以100作为异常分数
        let score = 0;
        if (typeof event.level === 'number') {
          score = Math.round(event.level * 100); // 0-1 转换为 0-100
          score = Math.max(0, Math.min(100, score)); // 确保在有效范围内
        } else {
          console.warn(`⚠️  事件level不是数字:`, event);
        }

        dataPoints.push(score);
        xAxisLabels.push(`帧${event.frame_no}`);
        // 保存包含帧号和分数的完整数据点，用于图表绘制
        chartDataPoints.push({ frame_no: event.frame_no, score: score });

        console.log(`  帧${event.frame_no}: level=${event.level}, 转换后分数=${score}%`);
      });

      // 显示所有数据点，不进行采样
      console.log(`📊 显示所有数据点，共${dataPoints.length}个`);

      // 确保数据点和标签数量一致
      if (dataPoints.length !== xAxisLabels.length || dataPoints.length !== chartDataPoints.length) {
        console.error('❌ 数据点、标签和完整数据点数量不一致:', dataPoints.length, 'vs', xAxisLabels.length, 'vs', chartDataPoints.length);
        // 确保所有数组长度一致
        const minLength = Math.min(dataPoints.length, xAxisLabels.length, chartDataPoints.length);
        dataPoints = dataPoints.slice(0, minLength);
        xAxisLabels = xAxisLabels.slice(0, minLength);
        chartDataPoints = chartDataPoints.slice(0, minLength);
      }

      console.log('🔄 更新anomalyScores、chartXAxis和chartDataPoints数据');
      this.anomalyScores = dataPoints;
      this.chartXAxis = xAxisLabels;
      this.chartDataPoints = chartDataPoints;

      console.log('✅ 异常分数图表数据生成完成:', this.anomalyScores.length, '个数据点');
      console.log('📊 数据点:', this.anomalyScores);
      console.log('⏰ 时间轴:', this.chartXAxis);
      console.log('📊 完整数据点:', this.chartDataPoints);

      // 输出详细统计信息
      const validScores = dataPoints.filter(score => score > 0);
      if (validScores.length > 0) {
        const avgScore = validScores.reduce((sum, score) => sum + score, 0) / validScores.length;
        const maxScore = Math.max(...validScores);
        const minScore = Math.min(...validScores);
        console.log(`📈 统计信息: 平均=${avgScore.toFixed(1)}%, 最高=${maxScore}%, 最低=${minScore}%`);
      }

      // 触发图表更新
      this.$nextTick(() => {
        console.log('🔄 触发图表更新，当前anomalyScores:', this.anomalyScores);
      });
    },

    updateAnomalyEventsList() {
      if (!this.viewListData || this.viewListData.length === 0) {
        this.anomalyEvents = [];
        this.currentHighRiskEvent = null;
        return;
      }

      console.log('🔍 开始更新异常事件列表，事件数:', this.viewListData.length);
      console.log('📊 当前异常阈值:', this.anomalyThreshold + '%');
      console.log('🎬 当前播放帧:', this.currentFrame);

      // 按帧号排序并格式化事件数据
      this.anomalyEvents = this.viewListData
        .sort((a, b) => a.frame_no - b.frame_no)
        .map(event => {
          // 确保level值正确处理（可能是0-1或0-10）
          let confidence = 50; // 默认值
          if (typeof event.level === 'number') {
            if (event.level <= 1) {
              confidence = Math.round(event.level * 100); // 0-1转换为0-100
            } else {
              confidence = Math.round(event.level * 10); // 0-10转换为0-100
            }
          }

          // 确保confidence在0-100范围内
          confidence = Math.max(0, Math.min(100, confidence));

          return {
            frameNo: event.frame_no,
            time: this.formatTime(event.frame_no / this.frameRate),
            eventName: event.event_name,
            level: event.level,
            description: event.description,
            createTime: event.create_time,
            summary: event.summary,
            riskLevel: this.getRiskLevel(event.level),
            confidence: confidence // 添加转换后的置信度
          };
        });

      // 筛选出大于等于当前异常阈值且帧号小于等于当前播放帧的事件
      // 特别处理：当currentFrame为0时，只显示第0帧的事件
      const thresholdEvents = this.anomalyEvents.filter(e => {
        const frameCondition = this.currentFrame === 0 ? e.frameNo === 0 : e.frameNo <= this.currentFrame;
        return e.confidence >= this.anomalyThreshold && frameCondition;
      });
      console.log(`📋 筛选出${thresholdEvents.length}个大于等于阈值(${this.anomalyThreshold}%)且帧号符合条件的事件，当前帧: ${this.currentFrame}`);

      if (thresholdEvents.length > 0) {
        // 选择帧号最大的最新事件
        this.currentHighRiskEvent = thresholdEvents.reduce((prev, current) =>
          prev.frameNo > current.frameNo ? prev : current
        );
        console.log('🚨 选中最新的阈值事件:', {
          frame: this.currentHighRiskEvent.frameNo,
          confidence: this.currentHighRiskEvent.confidence + '%',
          eventName: this.currentHighRiskEvent.eventName
        });
      } else {
        // 如果没有符合条件的事件，设置为null
        this.currentHighRiskEvent = null;
        console.log('ℹ️  没有找到大于等于阈值的事件');
      }

      console.log('✅ 异常事件列表更新完成:', this.anomalyEvents.length, '个事件');
      console.log('📋 事件详情:', this.anomalyEvents.map(e => ({
        frame: e.frameNo,
        title: e.eventName,
        confidence: e.confidence,
        riskLevel: e.riskLevel
      })));

      if (this.currentHighRiskEvent) {
        console.log('🚨 当前高风险事件:', {
          frame: this.currentHighRiskEvent.frameNo,
          title: this.currentHighRiskEvent.eventName,
          confidence: this.currentHighRiskEvent.confidence,
          summary: this.currentHighRiskEvent.summary
        });
      }
    },

    getRiskLevel(level) {
      if (level >= 90) return '极高风险';
      if (level >= 80) return '高风险';
      if (level >= 60) return '中等风险';
      return '低风险';
    },

    exportReport() {
      console.log('📤 导出分析报告');
      const report = {
        timestamp: new Date().toISOString(),
        video: this.currentVideoName || this.getVideoNameFromPath(),
        totalFrames: this.totalFrames,
        detectedEvents: this.anomalyEvents.length,
        events: this.anomalyEvents,
        performance: {
          auc: this.aucScore,
          ap: this.apScore,
          threshold: this.anomalyThreshold
        }
      };

      const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = `anomaly_report_${Date.now()}.json`;
      a.click();
      URL.revokeObjectURL(url);
    },

    refreshChart() {
      console.log('🔄 刷新异常分数图表');
      // 获取当前视频名称
      const currentVideoName = this.currentVideoName || this.videoSrc.split('/').pop().split('.')[0];

      if (this.viewListData && this.viewListData.length > 0) {
        // 如果有真实数据，重新生成图表
        this.generateAnomalyScoresFromEvents();
      } else {
        // 否则使用模拟数据，传入当前视频名称
        this.generateMockAnomalyScores(currentVideoName);
        this.generateMockAnomalyEvents(currentVideoName);
      }
    },

    getEventColor(confidence) {
      if (confidence >= 80) return '#e74c3c'; // 高风险 - 红色
      if (confidence >= 60) return '#f39c12'; // 中风险 - 橙色
      return '#2ecc71'; // 低风险 - 绿色
    },

    generateMockAnomalyEvents(videoName = '') {
      console.log('生成模拟异常事件，视频名称:', videoName);

      // 根据视频名称生成不同的模拟数据
      let events = [];

      if (videoName.includes('fire')) {
        // 火灾相关事件
        events = [
          {
            title: '火灾检测',
            timeRange: '00:05:17 - 00:07:36',
            description: '检测到异常火焰',
            confidence: 86,
            severity: 'high'
          },
          {
            title: '烟雾扩散',
            timeRange: '00:08:41 - 00:09:58',
            description: '检测到大量烟雾',
            confidence: 72,
            severity: 'high'
          }
        ];
      } else if (videoName.includes('BigBuckBunny') || videoName.includes('Bunny')) {
        // 兔子视频 - 无异常事件
        events = [];
      } else if (videoName.includes('ElephantsDream') || videoName.includes('Elephant')) {
        // 大象视频 - 无异常事件
        events = [];
      } else {
        // 其他视频
        events = [
          {
            title: '正常场景',
            timeRange: '00:00:00 - 00:00:00',
            description: '未检测到异常',
            confidence: 0,
            severity: 'low'
          }
        ];
      }

      this.anomalyEvents = events;

      // 设置当前高危事件（如果有）
      this.currentHighRiskEvent = events.length > 0 && events[0].severity === 'high' ? events[0] : null;
    },

    generateMockAnomalyScores(videoName = '') {
      console.log('生成模拟异常分数，视频名称:', videoName);

      let scores = [];

      if (videoName.includes('fire')) {
        // 火灾视频 - 有异常分数
        scores = [30, 15, 75, 65, 85, 72, 60, 55, 45, 35, 65, 75, 80, 70, 60, 50, 40, 30, 25, 20];
      } else {
        // 其他视频 - 低异常分数或零
        scores = Array.from({ length: 20 }, () => Math.random() * 20);
      }

      this.anomalyScores = scores;

      // 更新图表X轴时间 - 减少标签数量，避免拥挤
      const currentTime = new Date();
      this.chartXAxis = Array.from({ length: 3 }, (_, i) => {
        const time = new Date(currentTime.getTime() - (2 - i) * 60000);
        return time.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
      });
    }
  },
  mounted() {
    console.log('🎯 视频异常检测分析系统已加载');

    // 不自动生成默认模拟数据，等待视频加载时再加载对应数据

    // 从当前视频路径提取名称并加载对应数据
    const videoName = this.videoSrc.split('/').pop().split('.')[0];
    console.log('加载默认视频数据，视频名称:', videoName);
    this.loadViewListData(videoName);

    // 定期更新检测时间
    setInterval(() => {
      this.detectionTime = new Date().toLocaleString('zh-CN');
    }, 1000);

    // 加载在线视频列表
    this.loadOnlineVideos();
  },
  beforeUnmount() {
    if (this.animationFrameId) {
      cancelAnimationFrame(this.animationFrameId);
    }
    if (this.mockAnomalyInterval) {
      clearInterval(this.mockAnomalyInterval);
    }
  }
}
</script>

<style scoped>
/* 基础样式 - 基于demo.html的主题 */
:root {
  --primary-color: #3b59db;
  --secondary-bg: #f5f7fa;
  --dark-bg: #222;
  --light-bg: #fff;
  --text-dark: #333;
  --text-light: #fff;
  --border-color: #ddd;
  --danger: #e74c3c;
  --warning: #f39c12;
  --success: #2ecc71;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

.video-anomaly-detection-system {
  background-color: var(--secondary-bg);
  color: var(--text-dark);
  min-height: 100vh;
}

/* 头部导航 */
.header {
  background-color: var(--dark-bg);
  color: var(--text-light);
  padding: 12px 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}

.header .logo {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 18px;
  font-weight: bold;
}

.header .logo i {
  font-size: 20px;
}

.header .nav-right {
  display: flex;
  gap: 20px;
  align-items: center;
}

.header .nav-item {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
}

.header .nav-item i {
  font-size: 16px;
}

/* 主容器 */
.container {
  display: flex;
  gap: 20px;
  padding: 20px;
  max-width: 1400px;
  margin: 0 auto;
}

/* 侧边栏 */
.sidebar {
  width: 320px;
  background-color: var(--light-bg);
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
  height: fit-content;
}

.sidebar h3 {
  margin-bottom: 15px;
  font-size: 14px;
  color: #666;
}

.form-control {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--border-color);
  border-radius: 4px;
  font-size: 14px;
  margin-bottom: 20px;
}

.slider-container {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 20px;
}

.slider {
  -webkit-appearance: none;
  width: 100%;
  height: 8px;
  border-radius: 4px;
  background: #e0e0e0;
  outline: none;
}

.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #4CAF50;
  cursor: pointer;
  border: 2px solid white;
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
}

.btn-group {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
}

.btn {
  padding: 10px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
  transition: all 0.2s;
  flex: 1;
}

.btn-primary {
  background-color: var(--primary-color);
  color: white;
}

.btn-secondary {
  background-color: #e0e0e0;
  color: #333;
}

.btn:hover {
  opacity: 0.9;
}

/* 性能指标卡片 */
.performance-card {
  background-color: var(--dark-bg);
  color: var(--text-light);
  padding: 15px;
  border-radius: 8px;
  margin-bottom: 20px;
}

.performance-card h4 {
  margin-bottom: 10px;
  font-size: 14px;
}

.metrics {
  display: flex;
  justify-content: space-between;
}

.metric {
  text-align: center;
}

.metric-value {
  font-size: 24px;
  font-weight: bold;
  margin: 5px 0;
}

.metric-label {
  font-size: 12px;
  color: #aaa;
}

/* 侧边栏视频选择器 */
.video-selector-sidebar {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--border-color);
}

.online-video-section {
  margin-bottom: 15px;
}

.online-url-input {
  width: 100%;
  padding: 6px 10px;
  border: 1px solid var(--border-color);
  border-radius: 4px;
  font-size: 12px;
  margin-bottom: 8px;
}

.load-online-btn {
  width: 100%;
  padding: 6px 12px;
  background-color: var(--primary-color);
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 12px;
}

.local-videos-sidebar {
  margin-bottom: 15px;
}

.video-btn-sidebar {
  width: 100%;
  padding: 8px;
  margin-bottom: 5px;
  background-color: #f8f9fa;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  color: #495057;
  cursor: pointer;
  font-size: 12px;
  text-align: left;
}

.video-btn-sidebar:hover {
  background-color: #e9ecef;
}

.file-input-sidebar {
  width: 100%;
  font-size: 12px;
}

/* 帧检测控制 */
.frame-detection-controls {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--border-color);
}

.setting-item {
  display: flex;
  align-items: center;
  margin-bottom: 15px;
}

.setting-item label {
  width: 80px;
  font-weight: 600;
  color: #495057;
  font-size: 12px;
}

.frame-input,
.tolerance-input {
  width: 60px;
  padding: 6px 8px;
  border: 1px solid #ced4da;
  border-radius: 4px;
  margin-right: 8px;
  font-size: 12px;
}

.unit {
  color: #6c757d;
  font-size: 12px;
}

.frame-capture-checkbox {
  margin-right: 8px;
  transform: scale(1.1);
}

/* 关键帧管理 */
.keyframe-management {
  margin-bottom: 15px;
}

.keyframe-management h5 {
  margin-bottom: 10px;
  font-size: 12px;
  color: #666;
}

.keyframe-input-group {
  display: flex;
  gap: 8px;
  margin-bottom: 10px;
}

.keyframe-input {
  flex: 1;
  padding: 6px 8px;
  border: 1px solid #ced4da;
  border-radius: 4px;
  font-size: 12px;
}

.add-keyframe-btn {
  padding: 6px 12px;
  background-color: #28a745;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 12px;
}

.keyframe-list {
  max-height: 100px;
  overflow-y: auto;
}

.keyframe-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 4px 8px;
  background-color: #f8f9fa;
  border-radius: 4px;
  margin-bottom: 4px;
  font-size: 12px;
}

.keyframe-number {
  font-weight: 600;
  color: #495057;
}

.remove-keyframe-btn,
.seek-keyframe-btn {
  padding: 2px 6px;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  font-size: 11px;
}

.remove-keyframe-btn {
  background-color: #dc3545;
  color: white;
}

.seek-keyframe-btn {
  background-color: #17a2b8;
  color: white;
  margin-left: 4px;
}

.seek-btn,
.info-btn {
  width: 100%;
  padding: 8px;
  margin-bottom: 8px;
  background-color: #6c757d;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 12px;
}

.info-btn {
  background-color: #ffc107;
  color: #212529;
}

/* 主内容区 */
.main-content {
  flex: 1;
}

.video-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
  background-color: white;
  padding: 15px;
  border-radius: 8px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
}

.video-title {
  font-size: 18px;
  font-weight: bold;
  color: #333;
}

.status-badge {
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 600;
}

.status-ready {
  background-color: #e0e0e0;
  color: #666;
}

.status-analyzing {
  background-color: #cce5ff;
  color: #0056b3;
}

.status-detected {
  background-color: #f8d7da;
  color: #721c24;
}

.detection-time {
  font-size: 12px;
  color: #666;
}

/* 视频容器 */
.video-container {
  position: relative;
  width: 100%;
  height: 400px;
  background-color: #000;
  border-radius: 8px;
  overflow: hidden;
  margin-bottom: 10px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
}

.video-player {
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* 确保浏览器默认控制条可见 */
  controls: true;
}

.video-error {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background-color: rgba(220, 53, 69, 0.9);
  color: white;
  padding: 20px;
  border-radius: 8px;
  text-align: center;
  z-index: 10;
}

.retry-btn,
.sample-btn {
  padding: 8px 16px;
  margin: 5px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

.retry-btn {
  background-color: #ffc107;
  color: #212529;
}

.sample-btn {
  background-color: #28a745;
  color: white;
}

.frame-overlay {
  position: absolute;
  top: 20px;
  right: 20px;
  background-color: rgba(0, 123, 255, 0.9);
  color: white;
  padding: 15px;
  border-radius: 8px;
  z-index: 5;
  animation: fadeIn 0.3s ease-in-out;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.frame-info h3 {
  margin-bottom: 10px;
  font-size: 16px;
}

.frame-info p {
  margin: 5px 0;
  font-size: 14px;
}

.video-info {
  /* 移出视频容器，放在视频容器和图表容器之间，避免被覆盖 */
  position: relative;
  background: #f8f9fa;
  padding: 8px 12px;
  color: #495057;
  font-size: 12px;
  border-radius: 4px;
  margin-top: 5px;
  text-align: center;
  border: 1px solid #dee2e6;
  z-index: 1;
}

/* 图表容器 */
.chart-container {
  background-color: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
  margin-bottom: 20px;
}

.chart-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
}

.chart-title {
  font-size: 16px;
  font-weight: bold;
  color: #333;
}

.refresh-btn {
  background-color: #cce5ff;
  color: var(--primary-color);
  padding: 6px 12px;
  border-radius: 4px;
  font-size: 12px;
  cursor: pointer;
  border: none;
}

.chart {
  height: 200px;
  position: relative;
  background-color: white;
  border-radius: 4px;
  overflow: hidden;
}

.chart-line {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-size: contain;
  background-position: center;
  opacity: 1;
}

.chart-y-axis {
  position: absolute;
  left: 10px;
  top: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 10px 0;
  font-size: 12px;
  color: #666;
}

.chart-x-axis {
  position: absolute;
  bottom: 10px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: space-between;
  padding: 0 15px;
  font-size: 11px;
  color: #555;
  font-weight: 500;
  flex-wrap: nowrap;
  overflow-x: auto;
  white-space: nowrap;
  /* 增加标签间距，减少拥挤 */
  gap: 20px;
  /* 优化滚动体验 */
  scrollbar-width: thin;
}

.chart-x-axis::-webkit-scrollbar {
  height: 2px;
}

.chart-x-axis::-webkit-scrollbar-thumb {
  background-color: #ddd;
  border-radius: 1px;
}

.threshold-line {
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  background-color: #ff6b6b;
  z-index: 2;
}

.threshold-label {
  position: absolute;
  right: 15px;
  background-color: white;
  padding: 2px 6px;
  border-radius: 4px;
  font-size: 12px;
  color: #ff6b6b;
  font-weight: 600;
  z-index: 3;
}

/* 精确帧信息卡片 */
.exact-frame-info-card {
  background-color: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
  margin-bottom: 20px;
}

.exact-frame-info-card h4 {
  margin-bottom: 15px;
  font-size: 16px;
  color: #333;
}

.frame-detail p {
  margin: 8px 0;
  font-size: 14px;
  color: #666;
}

/* 分析结果区域 */
.analysis-sections {
  display: flex;
  gap: 20px;
}

.analysis-card {
  flex: 1;
  background-color: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
}

.analysis-card h3 {
  margin-bottom: 20px;
  font-size: 16px;
  font-weight: bold;
  color: #333;
}

.event-item {
  padding: 15px;
  border-bottom: 1px solid var(--border-color);
  margin-bottom: 15px;
}

.event-item:last-child {
  border-bottom: none;
  margin-bottom: 0;
}

.event-title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}

.event-title span:first-child {
  font-weight: bold;
  font-size: 14px;
  color: #333;
}

.event-title span:last-child {
  font-size: 12px;
  color: #666;
}

.event-description {
  font-size: 13px;
  color: #666;
  margin-bottom: 10px;
  line-height: 1.4;
}

.progress-bar {
  height: 6px;
  background-color: #eee;
  border-radius: 3px;
  margin-top: 8px;
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background-color: var(--primary-color);
  border-radius: 3px;
  transition: width 0.3s ease;
}

.progress-text {
  text-align: right;
  font-size: 12px;
  color: #666;
  margin-top: 5px;
  font-weight: 600;
}

.no-events {
  text-align: center;
  padding: 20px;
  color: #666;
  font-size: 14px;
}

.high-risk-event {
  background-color: #fff;
  padding: 20px;
  border-radius: 8px;
  border-left: 4px solid var(--danger);
}

.high-risk-title {
  font-size: 16px;
  font-weight: bold;
  color: var(--danger);
  margin-bottom: 8px;
}

.high-risk-time {
  font-size: 12px;
  color: #666;
  margin-bottom: 15px;
}

.event-desc-title {
  font-size: 14px;
  font-weight: bold;
  margin-bottom: 8px;
  color: #333;
}

.event-desc-content {
  font-size: 13px;
  line-height: 1.6;
  color: #666;
}

.event-summary-title {
  font-size: 14px;
  font-weight: bold;
  margin: 15px 0 8px 0;
  color: #333;
}

.event-summary-content {
  font-size: 13px;
  line-height: 1.6;
  color: #555;
  background-color: #f8f9fa;
  padding: 10px;
  border-radius: 4px;
  border-left: 3px solid var(--primary-color);
}

.no-high-risk {
  text-align: center;
  padding: 20px;
  color: #666;
  font-size: 14px;
}

/* 响应式设计 */
@media (max-width: 1200px) {
  .container {
    flex-direction: column;
  }

  .sidebar {
    width: 100%;
  }

  .analysis-sections {
    flex-direction: column;
  }
}

@media (max-width: 768px) {
  .header {
    flex-direction: column;
    gap: 10px;
  }

  .video-header {
    flex-direction: column;
    gap: 10px;
    align-items: flex-start;
  }

  .video-container {
    height: 250px;
  }

  .chart {
    height: 150px;
  }
}
</style>